/*! ******************************************************************************
 *
 * Created on 2021年9月25日
 *
 * Copyright(c) YuZhou Big Data Laboratory CO.,Ltd, 2022.  All rights reserved.
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.yzbdl.lanius.core.plugins.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Console;
import com.google.common.collect.Sets;
import org.pentaho.di.core.plugins.PluginTypeInterface;
import org.yzbdl.lanius.core.event.EventContext;
import org.yzbdl.lanius.core.exception.LaniusEngineDbException;
import org.yzbdl.lanius.core.plugins.event.PluginAddEvent;
import org.yzbdl.lanius.core.plugins.event.PluginCopyEvent;
import org.yzbdl.lanius.core.plugins.event.PluginDeleteEvent;
import org.yzbdl.lanius.core.plugins.event.PluginModifyEvent;
import org.yzbdl.lanius.core.plugins.metastore.*;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static org.yzbdl.lanius.core.plugins.service.PluginService.AnnoType.*;

/**
 * 插件数据业务
 *
 * @author hs@yzbdl.ac.cn
 * @version 1.0
 * @date 2019-06-11 10:40
 */
public class PluginService {

    private LdrPluginDao ldrPluginDao = LdrPluginDao.getInstance();

    private LdrCategoryDao ldrCategoryDao = LdrCategoryDao.getInstance();

    private LdrPluginCategoryDao ldrPluginCategoryDao = LdrPluginCategoryDao.getInstance();

    private static PluginService pluginService = new PluginService();

//    private LdrPluginClassificationDao ldrPluginClassificationDao = new LdrPluginClassificationDao();

    private PluginService() {
    }

    public static PluginService getInstance() {
        return PluginService.pluginService;
    }

    /**
     * 添加 PluginEntity
     *
     * @param entity
     */
    public void add(LdrPluginEntity entity) {
        try {
            ldrPluginDao.save(entity);
            EventContext.publish(new PluginAddEvent(entity));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改 PluginEntity
     *
     * @param entity
     */
    public void modify(LdrPluginEntity entity) {
        try {
            ldrPluginDao.update(entity);
            EventContext.publish(new PluginModifyEvent(entity));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<LdrPluginEntity> getPluginVersions(String pluginId) {
        List<LdrPluginEntity> ldrPluginEntityList = null;
        try {
            ldrPluginEntityList = ldrPluginDao.getPluginVersions(pluginId);
        } catch (LaniusEngineDbException e) {
            e.printStackTrace();
        }
        return ldrPluginEntityList;
    }

    /**
     * 删除 PluginEntity
     *
     * @param entity
     */
    public void delete(LdrPluginEntity entity) {
        try {
            ldrPluginDao.delete(entity.getId());
            EventContext.publish(new PluginDeleteEvent(entity));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除 LibPluginsEntity
     *
     * @param ids
     */
    public void deleteByListId(List<Integer> ids) {
        try {
            ldrPluginDao.deleteByListId(ids);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有已经逻辑删除的插件 PluginEntity
     *
     * @param
     */
    public List<LdrPluginEntity> getAllByIsValidFalse() {
        try {
            return ldrPluginDao.getAllByIsValidFalse();
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 获取所有某个分类下面所有 PluginEntity
     *
     * @param classificationId
     */
    public List<LdrPluginEntity> getAllByClassificationId(Integer classificationId) {
        try {
            return ldrPluginDao.getAllByClassificationId(classificationId);
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 获取所有某个分组下面所有 PluginEntity
     *
     * @param categoryId
     * @return
     */
    public List<LdrPluginEntity> getAllByCategoryId(Integer categoryId) {
        try {
            return ldrPluginDao.getAllByCategoryId(categoryId);
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 增加插件的使用次数
     *
     * @param pluginID
     * @param uses
     */
    public void increasePluginCount(String pluginID, String pluginTypeClass, int uses) {
        try {
            LdrPluginEntity plugin = ldrPluginDao.getLocalOne(pluginID, pluginTypeClass);
            // 记录插件累计使用次数（常用排序）
            int usageCount = plugin.getUsageCount();
            usageCount = usageCount + uses;
            plugin.setUsageCount(usageCount);
            ldrPluginDao.update(plugin);

            EventContext.publish(new PluginModifyEvent(plugin));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // /**
    // * 获取常用插件
    // *
    // * @param funcType 功能类型, 转换, 作业
    // * @return
    // */
    // public List<PluginEntity> getCommonlyPlugins(Integer funcType) {
    // try {
    // List<PluginEntity> pluginEntities = pluginDao.getPluginsOrderByUseCount(funcType);
    // return pluginEntities;
    // } catch (Exception e) {
    // e.printStackTrace();
    // return new ArrayList<>();
    // }
    // }

    /**
     * 获取常用插件
     *
     * @param pluginTypeClass 插件类别类全称
     * @return
     */
    public List<LdrPluginEntity> getCommonlyPlugins(Class<? extends PluginTypeInterface> pluginTypeClass) {
        try {
            List<LdrPluginEntity> pluginEntities = ldrPluginDao.getPluginsOrderByUseCount(pluginTypeClass.getName());
            return pluginEntities;
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    public void copyPluginTo(String originPluginId, String CategoryId) {
        try {
            LdrPluginCategoryEntity ldrPluginCategoryEntity = new LdrPluginCategoryEntity();
            ldrPluginCategoryEntity.setPluginId(Integer.valueOf(originPluginId));
            ldrPluginCategoryEntity.setCategoryId(Integer.valueOf(CategoryId));
            ldrPluginCategoryDao.save(ldrPluginCategoryEntity);

            LdrPluginEntity ldrPluginEntity = ldrPluginDao.getOne(Integer.valueOf(originPluginId));
            EventContext.publish(new PluginCopyEvent(ldrPluginEntity));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从分组中移除插件，解除关联关系
     *
     * @param pluginId
     * @param categoryId
     */
    public void removePlugin(String pluginId, String categoryId) {
        try {
            ldrPluginCategoryDao.delete(Integer.valueOf(pluginId), Integer.valueOf(categoryId));
            LdrPluginEntity ldrPluginEntity = ldrPluginDao.getOne(Integer.valueOf(pluginId));
            EventContext.publish(new PluginDeleteEvent(ldrPluginEntity));
        } catch (Exception var4) {
            var4.printStackTrace();
        }
    }

    /**
     * 卸载插件(逻辑删除，下次启动删除文件及记录)
     * <br/>
     * TODO 该方法需要修改, 根据插件的id、版本、类型找到插件，然后关闭其对应的类加载器，然后删除插件目录，然后清理一次依赖包。
     *
     * @param pluginId
     * @param classificationId
     */
    public void uninstallPlugin(String pluginId, String classificationId) {
        try {
            // 删除自定义分类的记录（如果存在）
//            ldrPluginClassificationDao.delete(Integer.valueOf(pluginId), Integer.valueOf(classificationId));

            // 根据插件查找是否存在依赖 如果该插件有依赖且只有该插件使用 更新或者 删除存在依赖关联记录，同时写入删除命令进入bat脚本
            // LibPluginsService.getInstance().removeLibPluginsAddBat(Integer.valueOf(pluginId));

            // 逻辑删除插件，将插件删除命令写入bat脚本
            LdrPluginEntity ldrPluginEntity = ldrPluginDao.getOne(Integer.valueOf(pluginId));
            ldrPluginEntity.setIsValid(false);
            ldrPluginDao.update(ldrPluginEntity);

            // List<Map<String, String>> delInfos = new ArrayList<>();
            // Map<String, String> delPlugin = new HashMap<>();
            // delPlugin.put("folderName", pluginEntity.getFolderName());
            // delPlugin.put("name", pluginEntity.getName());
            // delPlugin.put("type", "plugin");
            // delInfos.add(delPlugin);
            // FileUtils.jarNum += delInfos.size();
            // FileUtils.delPluginWithLibFile(new ArrayList<>(delInfos));

            // 刷新树
            EventContext.publish(new PluginDeleteEvent(ldrPluginEntity));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @return
     */
    public int getAllPlugin(String pluginName, String pluginType) {
        try {
            int localPluginSize = ldrPluginDao.getLocalPluginSize(pluginName, pluginType);
            return localPluginSize;
        } catch (LaniusEngineDbException e) {
            e.printStackTrace();
        }
        return 0;

    }

    /**
     * @param limit
     * @param offset
     * @param pluginName
     * @param pluginType
     * @return
     */
    public Set<String> getPageList(Integer limit, Integer offset, String pluginName, String pluginType) {
        Set<String> list = Sets.newHashSet();
        try {
            List<String> pluginEntities = ldrPluginDao.pageList(limit, offset, pluginName, pluginType);
            for (String entity : pluginEntities) {
                list.add(entity);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 根据插件Id查询一个插件
     *
     * @param pluginId
     * @return
     * @throws LaniusEngineDbException
     */
    public LdrPluginEntity findByPluginId(String pluginId) throws LaniusEngineDbException {
        return ldrPluginDao.findByPluginId(pluginId);
    }

    /**
     * 从插件元数据中添加一个插件
     *
     * @throws LaniusEngineDbException
     */
    public synchronized LdrPluginEntity addLocalPlugin(LdrPluginEntity ldrPluginEntity) throws LaniusEngineDbException {

        LdrClassificationEntity ldrClassificationEntity =
                ClassificationService.getInstance().addDefault(ldrPluginEntity.getPluginTypeClass());
        String category = ldrPluginEntity.getCategory();
        LdrCategoryEntity ldrCategoryEntity =
                CategoryService.getInstance().addLocalCategory(ldrClassificationEntity, category);

        LdrPluginEntity tempEntity = ldrPluginDao.getLocalVersionOne(ldrPluginEntity.getPluginId(),
                ldrPluginEntity.getPluginTypeName(), ldrPluginEntity.getVersion());
        if (tempEntity == null) {
            tempEntity = new LdrPluginEntity();
        }
        BeanUtil.copyProperties(ldrPluginEntity, tempEntity, new CopyOptions().ignoreNullValue());

        if (tempEntity.getId() == null) {
            ldrPluginDao.save(tempEntity);
        } else {
            ldrPluginDao.update(tempEntity);
        }

        LdrPluginCategoryEntity ldrPluginCategoryEntity =
                ldrPluginCategoryDao.getOne(tempEntity.getId(), ldrCategoryEntity.getId());
        if (ldrPluginCategoryEntity == null) {
            ldrPluginCategoryEntity = new LdrPluginCategoryEntity(tempEntity.getId(), ldrCategoryEntity.getId());
            ldrPluginCategoryDao.save(ldrPluginCategoryEntity);
        }

        // EventContext.publish(new PluginAddEvent(tempEntity));

        BeanUtil.copyProperties(tempEntity, ldrPluginEntity);
        return ldrPluginEntity;
    }

    /**
     * 添加本地插件，并建立与Category的关系
     * <p>
     * 1,如果数据库已经存在, 则直接查询返回 <br/>
     * 2,如果数据库还没有记录, 则先新增再返回.
     *
     * @return
     * @throws LaniusEngineDbException
     */
    public LdrPluginEntity addLocalPlugin(String pluginStrId, String name, String imageFile, String profile,
                                          String pluginFolderName, LdrCategoryEntity ldrCategoryEntity) throws LaniusEngineDbException {

        String pluginTypeClass = ldrCategoryEntity.getPluginTypeClass();
        LdrPluginEntity ldrPluginEntity = ldrPluginDao.getLocalOne(pluginStrId, pluginTypeClass);
        if (ldrPluginEntity == null) {
            ldrPluginEntity = new LdrPluginEntity();
        }

        ldrPluginEntity.setName(name);
        ldrPluginEntity.setPluginId(pluginStrId);
        // pluginEntity.setPluginImage(imageFile);
        ldrPluginEntity.setImage(imageFile);
        ldrPluginEntity.setChargeType(0);
        ldrPluginEntity.setProfile(profile);

        ldrPluginEntity.setPluginTypeName(pluginTypeClass);
        // pluginEntity.setFolderName(pluginFolderName);
        // pluginEntity.setFullPath(libraries.get(0));
        ldrPluginEntity.setIsValid(true); // 只要有插件包就默认可用

        if (ldrPluginEntity.getId() == null) {
            ldrPluginDao.save(ldrPluginEntity);
        } else {
            ldrPluginDao.update(ldrPluginEntity);
        }

        LdrPluginCategoryEntity ldrPluginCategoryEntity =
                ldrPluginCategoryDao.getOne(ldrPluginEntity.getId(), ldrCategoryEntity.getId());
        if (ldrPluginCategoryEntity == null) {
            ldrPluginCategoryEntity = new LdrPluginCategoryEntity(ldrPluginEntity.getId(), ldrCategoryEntity.getId());
            ldrPluginCategoryDao.save(ldrPluginCategoryEntity);
        }

        return ldrPluginEntity;
    }

    /**
     * @throws LaniusEngineDbException
     */
    public void addCloudPlugin(String pluginStrId, String name, int chargeType, String imageFile, String profile,
                               String versionId, String versionNumber, String groupName, Class<? extends PluginTypeInterface> pluginType,
                               int classificationId) throws LaniusEngineDbException {

        LdrPluginEntity ldrPluginEntity = new LdrPluginEntity();
        ldrPluginEntity.setName(name);
        ldrPluginEntity.setPluginId(pluginStrId);
        ldrPluginEntity.setChargeType(chargeType);
        ldrPluginEntity.setProfile(profile);
        // pluginEntity.setVersionId(versionId);
        // pluginEntity.setVersionNumber(versionNumber);
        ldrPluginEntity.setVersion(versionNumber);
        ldrPluginEntity.setPluginTypeName(pluginType.getName());
        // pluginEntity.setPluginImage(imageFile);
        ldrPluginEntity.setImage(imageFile);
        ldrPluginDao.save(ldrPluginEntity);

        LdrCategoryEntity ldrCategoryEntity = ldrCategoryDao.getOne(classificationId, groupName);
        if (ldrCategoryEntity == null) {
            ldrCategoryEntity = new LdrCategoryEntity();
            ldrCategoryEntity.setName(groupName);
            // categoryEntity.setFuncType(FuncType.getById(funcType).getId());
            ldrCategoryEntity.setPluginTypeClass(pluginType.getName());
            ldrCategoryEntity.setClassificationId(classificationId);
            ldrCategoryDao.save(ldrCategoryEntity);
        }

        LdrPluginCategoryEntity ldrPluginCategoryEntity = new LdrPluginCategoryEntity();
        ldrPluginCategoryEntity.setPluginId(ldrPluginEntity.getId());
        ldrPluginCategoryEntity.setCategoryId(ldrCategoryEntity.getId());
        ldrPluginCategoryDao.save(ldrPluginCategoryEntity);
    }

    /**
     * @param fullPath
     * @return
     * @throws LaniusEngineDbException
     */
    public List<LdrPluginEntity> getByFullPath(String fullPath) throws LaniusEngineDbException {
        return ldrPluginDao.getByFullPath(fullPath);
    }

    /**
     * 获取全部插件
     *
     * @return
     */
    public List<LdrPluginEntity> getAll() {
        try {
            return ldrPluginDao.getAll();
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    // /**
    // * 获取所有上次打开客户端使用过的插件
    // *
    // * @return
    // */
    // public List<LdrPluginEntity> getAllPluginsByUsageLastOpen() {
    // try {
    // return pluginDao.getAllPluginsByUsageLastOpen();
    // } catch (Exception e) {
    // e.printStackTrace();
    // return new ArrayList<>();
    // }
    // }
    //
    // /**
    // * 将使用次数置为0
    // *
    // */
    // public void updateUsageThisOpen() {
    // try {
    // pluginDao.updateUsageThisOpen();
    // } catch (Exception e) {
    // e.printStackTrace();
    // }
    // }

    /**
     * 获取指定类型的本地插件
     *
     * @param pluginTypeClass 插件类别类全称
     * @return
     */
    public List<LdrPluginEntity> getLocalPlugins(Class<? extends PluginTypeInterface> pluginTypeClass) {
        try {
            List<LdrPluginEntity> pluginEntities = ldrPluginDao.getLocals(pluginTypeClass);
            return pluginEntities;
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 获取指定类型的本地插件
     *
     * @param searchWord 插件关键字
     * @return
     */
    public List<LdrPluginEntity> searchPluginsWithNameOrId(String searchWord) {
        String likeSearchWord = "%" + searchWord + "%";
        try {
            // 使用关键字匹配名称和ID
            List<LdrPluginEntity> pluginEntities = ldrPluginDao.getPluginsWithNameOrId(likeSearchWord, likeSearchWord);
            return pluginEntities;
        } catch (Exception e) {
            Console.log("查找插件出错:" + e);
            return new ArrayList<>();
        }
    }

    /**
     * 获取所有某个分组下面所有 PluginEntity
     *
     * @param pluginAnnotationTypeName
     * @return
     */
    public List<LdrPluginEntity> getAllByPluginAnnotationTypeName(String pluginAnnotationTypeName) {
        try {
            return ldrPluginDao.getAllByPluginAnnotationTypeName(pluginAnnotationTypeName);
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 根据关键字查找插件列表
     *
     * @param key
     * @param value
     */
    public void showPluginList(String key, String value) {
        List<LdrPluginEntity> pluginList;
        AnnoType annotype = getByKey(key);
        switch (annotype) {
            case FIND:
                // 文件
                pluginList = searchPluginsWithNameOrId(value);
                break;
            case JOB:
                pluginList = getAllByPluginAnnotationTypeName(JOB.annoTypeCode);
                break;
            case TRANS:
                pluginList = getAllByPluginAnnotationTypeName(TRANS.annoTypeCode);
                break;
            case REPO:
                pluginList = getAllByPluginAnnotationTypeName(REPO.annoTypeCode);
                break;
            case DB:
                pluginList = getAllByPluginAnnotationTypeName(DB.annoTypeCode);
                break;
            case LANIUSRUN:
                pluginList = getAllByPluginAnnotationTypeName(LANIUSRUN.annoTypeCode);
                break;
            case COMPRESS:
                pluginList = getAllByPluginAnnotationTypeName(COMPRESS.annoTypeCode);
                break;
            default:
                pluginList = getAll();
        }
        for (LdrPluginEntity entity : pluginList) {
            Console.log("找到插件：" + entity.getName() + "      (" + entity.getPluginId() + "-" + entity.getVersion() + ")");
        }
    }

    enum AnnoType {
        /**
         * 插件搜索
         */
        FIND("find", "searchKeyWord"),
        /**
         * 作业插件
         */
        JOB("job", "org.pentaho.di.core.annotations.JobEntry"),
        /**
         * 转换插件
         */
        TRANS("trans", "org.pentaho.di.core.annotations.Step"),
        /**
         * 资源库插件
         */
        REPO("repository", "org.pentaho.di.core.annotations.RepositoryPlugin"),
        /**
         * 数据库插件
         */
        DB("database", "org.pentaho.di.core.annotations.DatabaseMetaPlugin"),
        /**
         * 执行器插件
         */
        LANIUSRUN("carte", "org.pentaho.di.core.plugins.CarteServlet"),
        /**
         * 压缩插件
         */
        COMPRESS("compress", "org.pentaho.di.core.compress.CompressionPlugin");


        /**
         * 类型关键字
         */
        public String annoTypeKey;

        /**
         * 类型Code
         */
        public String annoTypeCode;

        /**
         * @param annoTypeKey
         * @param annoTypeCode
         */
        AnnoType(String annoTypeKey, String annoTypeCode) {
            this.annoTypeKey = annoTypeKey;
            this.annoTypeCode = annoTypeCode;
        }

        public static AnnoType getByKey(String annoTypeKey) {
            for (AnnoType type : AnnoType.values()) {
                if (type.annoTypeKey.equals(annoTypeKey)) {
                    return type;
                }
            }
            return null;
        }

        public static AnnoType getByCode(String annoTypeCode) {
            for (AnnoType type : AnnoType.values()) {
                if (type.annoTypeCode.equals(annoTypeCode)) {
                    return type;
                }
            }
            return null;
        }
    }


}
